package com.treasure.mall.zbq.common.oss.impl;

import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import com.treasure.mall.zbq.common.StringUtilsExt;
import com.treasure.mall.zbq.common.constant.Constant;
import com.treasure.mall.zbq.common.oss.ResourceService;
import com.treasure.mall.zbq.common.sequence.SystemClock;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.net.*;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author allan
 * @date 2018-02-24
 */
@Service("resourceService")
public class OSSResourceServiceImpl implements ResourceService {


    @javax.annotation.Resource
    public Environment environment;
    private String bucketName;

    private String resourceDomain;

    private OSS ossClient;

    @Autowired
    public OSSResourceServiceImpl(Environment environment) {
        String endpoint, accessKeyId, accessKeySecret;
        try {
            endpoint = environment.getProperty(Constant.OSS_END_POINT, "");
            accessKeyId = environment.getProperty(Constant.OSS_ACCESS_KEY_ID, "");
            accessKeySecret = environment.getProperty(Constant.OSS_ACCESS_KEY_SECRET, "");
            bucketName = environment.getProperty(Constant.OSS_BUCKET_NAME, "");
            this.resourceDomain = environment.getProperty(Constant.OSS_RESOURCE_DOMAIN, "");
            ClientBuilderConfiguration configuration = new ClientBuilderConfiguration();
            configuration.setSupportCname(false);
            ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret, configuration);
        } catch (Exception ig) {
            bucketName = "";
            resourceDomain = "";
        }
    }

    @Override
    public Resource upload(String path, InputStream data) throws URISyntaxException {
        ossClient.putObject(bucketName, path, data);

        return get(path);
    }

    @Override
    public Resource get(String path) throws URISyntaxException {
        if (!StringUtils.isNotBlank(path)) {
            return null;
        }
        String url = path;
        if (!StringUtilsExt.isURl(path)) {
            url = resourceDomain + path;
        }
        return new OSSFileResource(new URI(url));
    }

    @Override
    public InputStream getInputStream(String url) {
        try {
            return ossClient.getObject(new URL(url), null).getObjectContent();
        } catch (MalformedURLException e) {
            return null;
        }
    }


    @Override
    public String getUrl(String path) {
        try {
            if (!StringUtilsExt.isURl(path)) {
                return get(path).toString();
            }
        } catch (URISyntaxException ignored) {

        }
        return path;
    }


    @Override
    public void delete(String path) {
        ossClient.deleteObject(bucketName, path);
    }


    /**
     * 批量下载为zip
     *
     * @param req
     * @param response
     * @param keyList  oss文件路径集合
     * @param fileName 下载出来的zip文件名（一般以合同名命名） 如：测试合同,zip
     */
    @Override
    public void downForZip(HttpServletRequest req, HttpServletResponse response,
                           List<String> keyList, String fileName) {
        // 创建临时文件
        File zipFile = null;
        try {
            //临时文件名称
            zipFile = File.createTempFile(SystemClock.INSTANCE.currentTime(), ".zip");

            FileOutputStream f = new FileOutputStream(zipFile);
            /**
             * 作用是为任何OutputStream产生校验和
             * 第一个参数是制定产生校验和的输出流，第二个参数是指定Checksum的类型 （Adler32（较快）和CRC32两种）
             */
            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            // 用于将数据压缩成Zip文件格式
            ZipOutputStream zos = new ZipOutputStream(csum);
            for (String ossFile : keyList) {
                // 获取Object，返回结果为OSSObject对象

                OSSObject ossObject = ossClient.getObject(bucketName, ossFile);
                // 读去Object内容  返回
                InputStream inputStream = ossObject.getObjectContent();
                // 对于每一个要被存放到压缩包的文件，都必须调用ZipOutputStream对象的putNextEntry()方法，确保压缩包里面文件不同名
                String name = ossFile.substring(ossFile.lastIndexOf("/") + 1);
                zos.putNextEntry(new ZipEntry(name));
                int bytesRead = 0;
                // 向压缩文件中输出数据
                while ((bytesRead = inputStream.read()) != -1) {
                    zos.write(bytesRead);
                }
                inputStream.close();
                zos.closeEntry(); // 当前文件写完，定位为写入下一条项目
            }
            zos.close();
            String header = req.getHeader("User-Agent").toUpperCase();
            if (header.contains("MSIE") || header.contains("TRIDENT") || header.contains("EDGE")) {
                fileName = URLEncoder.encode(fileName, "utf-8");
                //IE下载文件名空格变+号问题
                fileName = fileName.replace("+", "%20");
            } else {
                fileName = new String(fileName.getBytes(), "ISO8859-1");
            }
            response.reset();
            response.setContentType("text/plain");
            response.setContentType("application/octet-stream; charset=utf-8");
            response.setHeader("Location", fileName);
            response.setHeader("Cache-Control", "max-age=0");
            response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

            FileInputStream fis = new FileInputStream(zipFile);
            BufferedInputStream buff = new BufferedInputStream(fis);
            BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
            byte[] car = new byte[1024];
            int l = 0;
            while (l < zipFile.length()) {
                int j = buff.read(car, 0, 1024);
                l += j;
                out.write(car, 0, j);
            }
            // 关闭流
            fis.close();
            buff.close();
            out.close();

            ossClient.shutdown();
            // 删除临时文件
            zipFile.delete();
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public String uploadProgress(File file, String path, HttpServletRequest request) {
        try {
            /*
             * Determine whether the bucket exists
             */
            if (!ossClient.doesBucketExist(bucketName)) {
                /*
                 * Create a new OSS bucket
                 */
                System.out.println("Creating bucket " + bucketName + "\n");
                ossClient.createBucket(bucketName);
                CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
                createBucketRequest.setCannedACL(CannedAccessControlList.PublicRead);
                ossClient.createBucket(createBucketRequest);
            }

            /*
             * List the buckets in your account
             */
            System.out.println("Listing buckets");

            ListBucketsRequest listBucketsRequest = new ListBucketsRequest();
            listBucketsRequest.setMaxKeys(500);

            for (Bucket bucket : ossClient.listBuckets()) {
                System.out.println(" - " + bucket.getName());
            }

            /*
             * Upload an object to your bucket
             */
            System.out.println("Uploading a new object to OSS from a file\n");
			/*
			        这里用带进度条的OSS上传
			        将session传入PutObjectProgressListener的构造中!官网例子是没有这个操作的
			        注意new PutObjectRequest()的第三个参数是File而不是官网介绍的FileInputStream,否则获取不到进度.
	       */
            ossClient.putObject(new PutObjectRequest(bucketName, path, file).
                    withProgressListener(new PutObjectProgressListener(request.getSession(), path)));
        } catch (OSSException oe) {
            oe.printStackTrace();
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message: " + oe.getErrorCode());
            System.out.println("Error Code:       " + oe.getErrorCode());
            System.out.println("Request ID:      " + oe.getRequestId());
            System.out.println("Host ID:           " + oe.getHostId());
        } catch (ClientException ce) {
            ce.printStackTrace();
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message: " + ce.getMessage());
            System.out.println(ce.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            ossClient.shutdown();
        }
        return path;
    }

    @Override
    public int getUploadPercent(HttpServletRequest request, String path) {
        HttpSession session = request.getSession();
        int percent = session.getAttribute(path) == null ? 0 : (Integer) session.getAttribute(path);
        return percent;
    }

    @Override
    public String getResourceDomain() {
        return StringUtilsExt.rtrim(this.resourceDomain, '/') + "/";
    }
}
